#include "BasicType.h"

#include "Stream.h"

namespace LightPlayer{

	//////////////////////////////////////////////////////////////////////////
	// RECT
	//////////////////////////////////////////////////////////////////////////

	/*    o--------Max   */
	/*    |         |   */
	/*    |   AABB  |   */
	/*    |         |   */
	/*    Min-------o   */


	RECT::RECT( float _xmin /*= 0*/, float _xmax /*= 0*/, float _ymin /*= 0*/, float _ymax /*= 0*/ ){
		init(_xmin,_xmax,_ymin,_ymax);
	}

	void RECT::init(float _xmin /*= 0*/, float _xmax/* = 0*/, float _ymin /*= 0*/, float _ymax /*= 0*/){
		Xmin = _xmin; Xmax = _xmax; Ymin = _ymin; Ymax = _ymax;
	}

	void RECT::readFromStream( Stream* stream ){
		stream->align();
		uint8 BitsUsed = stream->read_uint(5);
		Xmin = TWIPS_TO_PIXELS( static_cast<float>(stream->read_sint( BitsUsed ) ) );
		Xmax = TWIPS_TO_PIXELS( static_cast<float>(stream->read_sint( BitsUsed ) ) );
		Ymin = TWIPS_TO_PIXELS( static_cast<float>(stream->read_sint( BitsUsed ) ) );
		Ymax = TWIPS_TO_PIXELS( static_cast<float>(stream->read_sint( BitsUsed ) ) );
	}

	void RECT::extend( const RECT& other ){
		if(! isNull() ){
			Xmin = Xmin < other.Xmin ? Xmin : other.Xmin;
			Ymin = Ymin < other.Ymin ? Ymin : other.Ymin;
			Xmax = Xmax > other.Xmax ? Xmax : other.Xmax;
			Ymax = Ymax > other.Ymax ? Ymax : other.Ymax;
		}
		else{
			Xmin = other.Xmin;
			Ymin = other.Ymin;
			Xmax = other.Xmax;
			Ymax = other.Ymax;
		}
		
	}

	void RECT::transform( const MATRIX& transformation )
	{
		OBB oriented_me;
		oriented_me = (*this);
		oriented_me.transform( transformation );
		(*this) = oriented_me;
	}

	void RECT::operator=( const RECT& o ){
		Xmin = o.Xmin;
		Ymin = o.Ymin;
		Xmax = o.Xmax;
		Ymax = o.Ymax;
	}

	void RECT::operator=( const OBB& o ){
		Xmin = o.GetMinX();
		Ymin = o.GetMinY();
		Xmax = o.GetMaxX();
		Ymax = o.GetMaxY();
	}

	/*    D---------C   */
	/*    |         |   */
	/*    |   OBB   |   */
	/*    |         |   */
	/*    A---------B   */

	OBB::OBB(){
		init();
	}

	void OBB::init(){
		A = B = C = D = vec2();
	}

	float OBB::width() const{
		return (B-A).length();
	}

	float OBB::height() const{
		return (D-A).length();
	}

	void OBB::transform( const MATRIX& transformation ){
		A = transformation * A;
		B = transformation * B;
		C = transformation * C;
		D = transformation * D;
	}

	float OBB::GetMinX() const{
		float tmp = A.x < B.x ? A.x : B.x;
		tmp = tmp < C.x ? tmp : C.x;
		tmp = tmp < D.x ? tmp : D.x;
		return tmp;
	}

	float OBB::GetMinY() const{
		float tmp = A.y < B.y ? A.y : B.y;
		tmp = tmp < C.y ? tmp : C.y;
		tmp = tmp < D.y ? tmp : D.y;
		return tmp;
	}

	float OBB::GetMaxX() const{
		float tmp = A.x > B.x ? A.x : B.x;
		tmp = tmp > C.x ? tmp : C.x;
		tmp = tmp > D.x ? tmp : D.x;
		return tmp;
	}

	float OBB::GetMaxY() const{
		float tmp = A.y > B.y ? A.y : B.y;
		tmp = tmp > C.y ? tmp : C.y;
		tmp = tmp > D.y ? tmp : D.y;
		return tmp;
	}

	void OBB::operator=( const OBB& o ){
		A = o.A; 
		B = o.B;
		C = o.C;
		D = o.D;
	}

	void OBB::operator=( const RECT& o ){
		A.set( o.Xmin, o.Ymin );
		B = A; 
		B.x += o.width();

		C.set( o.Xmax, o.Ymax );
		D = A; 
		D.y += o.height();
	}


	//////////////////////////////////////////////////////////////////////////
	// TAG
	//////////////////////////////////////////////////////////////////////////


	TAG::TAG( uint32 _type /*= 0*/, uint32 _size /*= 0*/ ){
		SetTagType(_type);
		size = _size;
	}

	void TAG::readFromStream( Stream* stream ){
		uint32 tag_header = stream->read_u16();
		SetTagType( tag_header >> 6 );
		size = tag_header & 0x3F;
		if (size == 0x3F) {
			size = stream->read_u32();
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// RGB / RGBA
	//////////////////////////////////////////////////////////////////////////

	/*
	RGB::RGB( uint8 _R , uint8 _G , uint8 _B ){
		R=_R;G=_G;B=_B;
	}
	*/
	void RGB::readFromStream( Stream* stream ){
		R = stream->read_u8();
		G = stream->read_u8();
		B = stream->read_u8();
	}


	RGBA::RGBA( uint8 _R /*= 0*/, uint8 _G /*= 0*/, uint8 _B /*= 0*/, uint8 _A /*= 0*/ ){
		R=_R;G=_G;B=_B;A=_A;
	}

	void RGBA::readFromStream( Stream* stream ){
		R = stream->read_u8();
		G = stream->read_u8();
		B = stream->read_u8();
		A = stream->read_u8();
	}

	void RGBA::readARGBFromStream( Stream* stream ){
		A = stream->read_u8();
		R = stream->read_u8();
		G = stream->read_u8();
		B = stream->read_u8();
	}

	void RGBA::readRGBFromStream( Stream* stream ){
		R = stream->read_u8();
		G = stream->read_u8();
		B = stream->read_u8();
		A = 255;
	}


	//////////////////////////////////////////////////////////////////////////
	// MATRIX
	//////////////////////////////////////////////////////////////////////////

	MATRIX::MATRIX(){
		setIdentity();
	}

	void MATRIX::setIdentity(){
		_11 = 1.0f; _12 = 0.0f; _13 = 0.0f; 
		_21 = 0.0f; _22 = 1.0f; _23 = 0.0f;
	}

	void MATRIX::setScaleMatrix( float scale_x, float scale_y ){
		setIdentity();
		_11 = scale_x;
		_22 = scale_y;
	}

	void MATRIX::setRotationMatrix(float angle){
		setIdentity();
		_11 = cos(angle);		_12 = -sin(angle);
		_21 = sin(angle);		_22 =  cos(angle);
	}

	void MATRIX::readFromStream( Stream* stream ){
		setIdentity();
		stream->align();
		bool has_scale = stream->read_bool();
		if(has_scale){
			uint8 scale_nbits = stream->read_uint(5);
			_11 = ( stream->read_sint(scale_nbits) / 65536.0f );
			_22 = ( stream->read_sint(scale_nbits) / 65536.0f );
		}
		bool has_rotate = stream->read_bool();
		if(has_rotate){
			uint8 rotate_nbits = stream->read_uint(5);
			_12 = stream->read_sint(rotate_nbits) / 65536.0f;
			_21 = stream->read_sint(rotate_nbits) / 65536.0f;
		}
		uint8 translate_nbits = stream->read_uint(5);
		if (translate_nbits > 0){
			_13 = TWIPS_TO_PIXELS( (float) stream->read_sint(translate_nbits) );
			_23 = TWIPS_TO_PIXELS( (float) stream->read_sint(translate_nbits) );
		}
	}

	vec2 MATRIX::operator*( const vec2& vec ) const{
		return vec2(	_11*vec.x + _12*vec.y + _13,
						_21*vec.x + _22*vec.y + _23 );
	}

	MATRIX MATRIX::operator*( const MATRIX& mat ) const{
		MATRIX result;

		result._11 = this->_11 * mat._11 + this->_12 * mat._21 + this->_13 * 0;
		result._12 = this->_11 * mat._12 + this->_12 * mat._22 + this->_13 * 0;
		result._13 = this->_11 * mat._13 + this->_12 * mat._23 + this->_13 * 1;

		result._21 = this->_21 * mat._11 + this->_22 * mat._21 + this->_23 * 0;
		result._22 = this->_21 * mat._12 + this->_22 * mat._22 + this->_23 * 0;
		result._23 = this->_21 * mat._13 + this->_22 * mat._23 + this->_23 * 1;

		return result;
	}

	vec2 MATRIX::convertToMatrixCoord( const vec2& vec ) const{
		vec2 matPosToVec( vec.x - _13, vec.y - _23 );

		MATRIX copy = (*this);
		copy._13 = copy._23 = 0.0f;

		copy.transpose();
		copy.invert();

		return (copy*matPosToVec);
	}

	void MATRIX::multBy( float k ){
		_11 *= k;
		_12 *= k;
		_21 *= k;
		_22 *= k;
	}

	void MATRIX::invertByMinor(){
		float __11 = _11;
		float __12 = _12;
		float __21 = _21;
		float __22 = _22;

		_11 = __22; 
		_12 = __21;
		_21 = __12;
		_22 = __11;
	}

	void MATRIX::transpose(){
		float __11 = _11;
		float __12 = _12;
		float __21 = _21;
		float __22 = _22;
		
		_11 = __11; 
		_21 = __12;
		_12 = __21;
		_22 = __22;
	}

	float MATRIX::determinant() const{
		return (_11*_22 - _12*_21);
	}

	void MATRIX::invert(){
		float d = determinant(); 
		invertByMinor();
		_12 *= -1;
		_21 *= -1;
		transpose();
		multBy( 1/d );
	}

	vec2	MATRIX::getColumn0() const						{ return vec2(_11, _12); };
	vec2	MATRIX::getColumn1() const						{ return vec2(_21, _22); };
	vec2	MATRIX::getColumn2() const						{ return vec2(_13, _23); };
	void	MATRIX::setColumn0(const vec2& AxisX)			{ _11 = AxisX.x; _12 = AxisX.y; };
	void	MATRIX::setColumn1(const vec2& AxisY)			{ _21 = AxisY.x; _22 = AxisY.y; };
	void	MATRIX::setColumn2(const vec2& Translation)		{ _13 = Translation.x; _23 = Translation.y; };

	void	MATRIX::setX(float X) { _13 = X; }
	void	MATRIX::setY(float Y) { _23 = Y; }
	float	MATRIX::getX() const  { return _13; }
	float	MATRIX::getY() const  { return _23; }

	void	MATRIX::setScaleX(float X)	{ vec2 Xaxis = getColumn0(); Xaxis = Xaxis / Xaxis.length(); setColumn0( Xaxis * X ); }
	void	MATRIX::setScaleY(float Y)	{ vec2 Yaxis = getColumn1(); Yaxis = Yaxis / Yaxis.length(); setColumn1( Yaxis * Y ); }
	float	MATRIX::getScaleX() const	{ return getColumn0().length(); }
	float	MATRIX::getScaleY() const	{ return getColumn1().length(); }

	float	MATRIX::getRotation() const				{ vec2 Xaxis = getColumn0(); Xaxis = Xaxis / Xaxis.length(); return atan2(Xaxis.y, Xaxis.x); }
	void	MATRIX::setRotation(float angle)		{	
		float act_angle = getRotation(); 
		float delta_angle = act_angle - angle; 
		MATRIX delta_matrix; delta_matrix.setRotationMatrix(delta_angle); 
		(*this) = (*this) * delta_matrix; 
	}

	//////////////////////////////////////////////////////////////////////////
	// CXFORM
	//////////////////////////////////////////////////////////////////////////

	CXFORM::CXFORM(float mr, float mg, float mb, float ma,
				float ar, float ag, float ab, float aa){
		init(mr,mg,mb,ma,ar,ag,ab,aa);
	}

	void CXFORM::init(float mr, float mg, float mb, float ma,
				float ar, float ag, float ab, float aa){
		_MultR = mr; _MultG = mg; _MultB = mb; _MultA = ma;
		_AddR = ar;  _AddG = ag;  _AddB = ab;  _AddA = aa;
	}

	void CXFORM::readRGBFromStream( Stream* stream ){
		init();
		stream->align();

		bool has_add = stream->read_bool();
		bool has_mult = stream->read_bool();
		int	nbits = stream->read_uint(4);

		if (has_mult) {
			_MultR = stream->read_sint(nbits) / 256.0f;
			_MultG = stream->read_sint(nbits) / 256.0f;
			_MultB = stream->read_sint(nbits) / 256.0f;
		}
		if (has_add) {
			_AddR = (float) stream->read_sint(nbits) / 256.0f;
			_AddG = (float) stream->read_sint(nbits) / 256.0f;
			_AddB = (float) stream->read_sint(nbits) / 256.0f;
		}
	}

	void CXFORM::readRGBAFromStream( Stream* stream ){
		init();
		stream->align();

		bool has_add = stream->read_bool();
		bool has_mult = stream->read_bool();
		int	nbits = stream->read_uint(4);

		if (has_mult) {
			_MultR = stream->read_sint(nbits) / 256.0f;
			_MultG = stream->read_sint(nbits) / 256.0f;
			_MultB = stream->read_sint(nbits) / 256.0f;
			_MultA = stream->read_sint(nbits) / 256.0f;
		}
		if (has_add) {
			_AddR = (float) stream->read_sint(nbits) / 256.0f;
			_AddG = (float) stream->read_sint(nbits) / 256.0f;
			_AddB = (float) stream->read_sint(nbits) / 256.0f;
			_AddA = (float) stream->read_sint(nbits) / 256.0f;
		}
	}

	CXFORM CXFORM::operator*( const CXFORM& other ) const{
		CXFORM oTmp;
		oTmp._MultR = other._MultR * _MultR;
		oTmp._MultG = other._MultG *_MultG;
		oTmp._MultB = other._MultB *_MultB;
		oTmp._MultA = other._MultA *_MultA;
		oTmp._AddR  = other._MultR * _AddR +  other._AddR;
		oTmp._AddG  = other._MultG * _AddG + other._AddG;
		oTmp._AddB  = other._MultB * _AddB + other._AddB;
		oTmp._AddA  = other._MultA * _AddA + other._AddA;
		return oTmp;
	}

	const CXFORM& CXFORM::operator*=( const CXFORM& other ){
		(*this) = (*this) * other;
		return (*this);
	}


};